基于STM32F103自制CMSIS

您所在的位置:网站首页 gd32f103 stlink 基于STM32F103自制CMSIS

基于STM32F103自制CMSIS

2024-07-07 05:20| 来源: 网络整理| 查看: 265

关注+星标公众号,不错过精彩内容

编排 | strongerHuang

微信公众号 | strongerHuang

市面上针对Cortex-M处理器的下载器,有很多是基于CMSIS-DAP演变而来,比如:e-Link、GD-Link等。

之前给大家分享过自制ST-Link的教程,今天继续为大家分享一篇:基于STM32F103C8,自制CMSIS-DAP下载器。

1

关于CMSIS-DAP

CMSIS-DAP是支持访问 CoreSight 调试访问端口(DAP)的固件规范和实现,以及各种Cortex处理器提供CoreSight调试和跟踪。

地址:

https://arm-software.github.io/CMSIS_5/DAP/html/index.html

CMSIS-DAP固件作为源代码提供,并且可以完全配置为新的调试单元。

这里相关的更多内容,可以参看我之前分享过的一篇文章:Cortex-M软件接口标准CMSIS那些重要内容。

2

CMSIS-DAP固件

CMSIS-DAP固件Arm以源码形式提供,不存在版权问题(因为针对Arm Cortex处理器,他们还希望更多人使用)。

1.固件版本

目前有两个版本:

版本1配置使用USB HID作为与主机PC的接口。

版本2配置使用WinUSB作为与主机PC的接口,并提供高速SWO跟踪流。

2.源码位置

目前源码提供在Keil MDK V5版本,安装好Keil MDK,你在安装目录下就能找到源码。

C:\Keil_v5\ARM\Packs\ARM\CMSIS\5.7.0\CMSIS\DAP\Firmware

(目前MDK V5.33,CMSIS版本为5.7.0)

3.源码描述

从文件目录可以看出,官方源码提供了一些模板和例子。

目前只提供了LPC处理器的例子,如果你有这个处理器对应的板卡,可以直接使用该源码做一个下载调试器。(下面就针对于LPC这个例子进行“改装”)

3

配置

利用STM32CubeMX图形化配置工具,帮助用户选择单片机引脚的功能,并自动生成外设初始化代码。配置了USB、SPI1和USART1,并选择了USB的Custom HID middleware模式。GPIOB10到GPIO15被配置为JTAG调试需要的引脚。GPIOC13用于驱动单片机上的LED灯。

ST公司也开发了他们自己的JTAG调试器——STLink。当然它并不是必要的,你也可以使用J-Link或者其他种类的调试器。STLink的驱动和程序可以在ST官网上下载。在网站里还有一个基于Eclipse开发环境开发的IDE,STM32CubeMX也被包含其中。我选择的IDE是基于CodeBlocks的Embitz,IDE中的arm_none_eabi_gcc版本是5.4.1。在我完成我的CMSIS-DAP之前,我必须使用STLink来调试我的代码。现在我在使用新做出来的CMSIS-DAP结合OpenOCD进行日常的开发。

4

从CMSIS-DAP的源码开始

源码可以在官网下载:

https://github.com/ARM-software/CMSIS_5

也可以直接在 Keil MDK 安装目录下获取:

C:\Keil_v5\ARM\Packs\ARM\CMSIS\5.7.0\CMSIS\DAP

将从示例V1的头文件 DAP_config.h 开始分析。

选择LPC-Link-II V1作为我的参考是因为它是通过USB HID实现的(V2是通过WinUSB实现)。我分析的第一个文件是DAP_Config.h。第一个关键位置如下:

#ifdef _RTE_ #include "RTE_Components.h" #include CMSIS_device_header #else #include "device.h" #endif

不用RTE的相关文件,创建我自己的device.h。

我将参数CPU_CLOCK重定义为72000000(72MHz)。根据文件里的注释,参数DAP_PACKET_SIZE必须重新定义为64U。我把SWO_UART改为0,这让我的工作轻松不少。参数TIMESTAMP_CLOCK也要重定义为72000000。LPC-Link-II使用Cortex-M3 的 DWT模块实现时间戳(TIMESTAMP),这也是为什么我想在CubeMX中尝试配置STM32F103的DWT。最后我自己写了一小段代码来实现这个功能(在device.c中):

CoreDebug->DEMCR |= CoreDebug_DEMCR_TRCENA_Msk; /** * On Cortex-M7 core there is a LAR register in DWT domain. * Any time we need to setup DWT registers, we MUST write * 0xC5ACCE55 into LAR first. LAR means Lock Access Register. */ DWT->CYCCNT = 0; DWT->CTRL |= DWT_CTRL_CYCCNTENA_Msk;

我定义的引脚和NXP LPCxx的完全不同。我为STM32F103重写了所有的引脚的操作代码。在DAP_Config.h这个文件中还有一些奇怪的地方,比如:

// SWCLK/TCK I/O pin ------------------------------------- /** SWCLK/TCK I/O pin: Get Input. \return Current status of the SWCLK/TCK DAP hardware I/O pin. */ __STATIC_FORCEINLINE uint32_t PIN_SWCLK_TCK_IN (void) { return ((LPC_GPIO_PORT->PIN[PIN_SWCLK_TCK_PORT]>> PIN_SWCLK_TCK_BIT) & 1U); }

我不明白为什么DAP的代码里需要读取SWCLK/TCK引脚的电平,这个引脚明明是被配置为推挽输出来产生时钟信号输送给JTAG从机的。从上面列出来的代码可以看出,它是希望返回当前引脚的电平值,我的实现方式稍微有点不同:

__STATIC_FORCEINLINE uint32_t PIN_SWCLK_TCK_IN (void) { return (uint32_t)(JTAG_TCK_GPIO_Port->ODR & JTAG_TCK_Pin ? 1:0); }

我返回的是当前引脚的输出值。我不确定这是否正确。在整个代码中,这句话只被DAP.c中的一个叫DAP_SWJ_Pins的函数调用了两次。我猜测DAP_SWJ_Pins这个函数是用来测试IO口是否工作正常的。

另一个奇怪的地方是PIN_nRESET_OUT:

/** nRESET I/O pin: Set Output. \param bit target device hardware reset pin status: - 0: issue a device hardware reset. - 1: release device hardware reset. */ __STATIC_FORCEINLINE void PIN_nRESET_OUT (uint32_t bit) { if (bit) { LPC_GPIO_PORT->DIR[PIN_nRESET_PORT] &= ~(1U pUserData)->InEvent(hhid->Report_buf[0], hhid->Report_buf[1]); return USBD_OK; }

设备描述符CUSTOM_HID_ReportDesc_FS被定义在usbd_suctom_hid_if.c中,我定义了一个简单的描述符:

/** Usb HID report descriptor. */ __ALIGN_BEGIN static uint8_t CUSTOM_HID_ReportDesc_FS[USBD_CUSTOM_HID_REPORT_DESC_SIZE] __ALIGN_END = { /* USER CODE BEGIN 0 */ /* A minimal Report Desc with INPUT/OUTPUT/FEATURE report. Zach Lee */ 0x06,0x00,0xFF, /* Usage Page (vendor defined) ($FF00) global */ 0x09,0x01, /* Usage (vendor defined) ($01) local */ 0xA1,0x01, /* Collection (Application) */ 0x15,0x00, /* LOGICAL_MINIMUM (0) */ 0x25,0xFF, /* LOGICAL_MAXIMUM (255) */ 0x75,0x08, /* REPORT_SIZE (8bit) */ // Input Report 0x95,64, /* Report Length (64 REPORT_SIZE) */ 0x09,0x01, /* USAGE (Vendor Usage 1) */ 0x81,0x02, /* Input(data,var,absolute) */ // Output Report 0x95,64, /* Report Length (64 REPORT_SIZE) */ 0x09,0x01, /* USAGE (Vendor Usage 1) */ 0x91,0x02, /* Output(data,var,absolute) */ // Feature Report 0x95,64, /* Report Length (64 REPORT_SIZE) */ 0x09,0x01, /* USAGE (Vendor Usage 1) */ 0xB1,0x02, /* Feature(data,var,absolute) */ /* USER CODE END 0 */ 0xC0 /* END_COLLECTION */ };

可能Feature Report在CMSIS-DAP中不是必要的,就留着它吧。

我在这个C文件中还实现了一个新的接口函数CUSTOM_HID_InEvent_FS:

static int8_t CUSTOM_HID_InEvent_FS(uint8_t event_idx, uint8_t state); /* An extra interface func. */ USBD_CUSTOM_HID_ItfTypeDef USBD_CustomHID_fops_FS = { CUSTOM_HID_ReportDesc_FS, CUSTOM_HID_Init_FS, CUSTOM_HID_DeInit_FS, CUSTOM_HID_OutEvent_FS, /* I add an extra interface func below. Zach Lee */ CUSTOM_HID_InEvent_FS }; extern void USBD_OutEvent(void); /* Implemented in file "device.h" */ static int8_t CUSTOM_HID_OutEvent_FS(uint8_t event_idx, uint8_t state) { /* USER CODE BEGIN 6 */ USBD_OutEvent(); /* OUTPUT REPORT was received. Zach Lee */ return (USBD_OK); /* USER CODE END 6 */ } extern void USBD_InEvent(void); /* Implemented in file "device.h" */ static int8_t CUSTOM_HID_InEvent_FS(uint8_t event_idx, uint8_t state) { /* USER CODE BEGIN extra */ USBD_InEvent(); /* INPUT REPORT has been sent. Zach Lee */ return (USBD_OK); /* USER CODE END extra */ }

CUSTOM_HID_Init_FS和CUSTOM_HID_DeInit_FS两个函数被实现为使能/失能DAP功能:

extern void USBD_HID0_Initialize (void); static int8_t CUSTOM_HID_Init_FS(void) { /* USER CODE BEGIN 4 */ USBD_HID0_Initialize(); /* Initialize USB communication of DAP. Zach Lee */ return (USBD_OK); /* USER CODE END 4 */ } extern void USBD_HID0_Uninitialize (void); static int8_t CUSTOM_HID_DeInit_FS(void) { /* USER CODE BEGIN 5 */ USBD_HID0_Uninitialize(); /* Uninitialize. Zach Lee */ return (USBD_OK); /* USER CODE END 5 */ }

6

编写将所有源代码关联起来的桥梁

为了移除CMSIS RTOS,我写了一些函数来模拟RTOS:

/** * Replace CMSIS RTOS api */ static volatile int osFlags; /* Use "volatile" to prevent GCC optimizing the code. */ void osKernelInitialize(void) { osFlags = 0; return; } int osThreadFlagsWait(int mask, int b, int c) { (void)b; (void)c; int ret; while((osFlags&mask) == 0) { ; } ret = osFlags; osFlags &= ~mask; return ret; } void osThreadFlagsSet(int tid, int f) { (void)tid; osFlags |= f; return; }

函数USBD_Configured和USBD_HID_GetReportTrigger实现如下:

intUSBD_Configured(int n){ (void)n; return(hUsbDeviceFS.dev_state == USBD_STATE_CONFIGURED ?1:0);} void USBD_HID_GetReportTrigger(int a, int b, void * report, int len) { (void)a; (void)b; if (USBD_OK != USBD_CUSTOM_HID_SendReport(&hUsbDeviceFS, report, len)) { ; } return; }

函数USBD_CUSTOM_HID_SendReport是由STM32CubeMX生成的,它被定义在usbd_customhid.c中,我自己的事件句柄如下:

bool USBD_HID0_SetReport (uint8_t rtype, uint8_t req, uint8_t rid, const uint8_t *buf, int32_t len); void USBD_OutEvent(void) { USBD_CUSTOM_HID_HandleTypeDef *hhid = (USBD_CUSTOM_HID_HandleTypeDef *)hUsbDeviceFS.pClassData; USBD_HID0_SetReport(HID_REPORT_OUTPUT, 0, 0, hhid->Report_buf, USBD_CUSTOMHID_OUTREPORT_BUF_SIZE); } int32_t USBD_HID0_GetReport (uint8_t rtype, uint8_t req, uint8_t rid, uint8_t *buf); void USBD_InEvent(void) { int32_t len; USBD_CUSTOM_HID_HandleTypeDef *hhid = (USBD_CUSTOM_HID_HandleTypeDef *)hUsbDeviceFS.pClassData; if ((len=USBD_HID0_GetReport(HID_REPORT_INPUT, USBD_HID_REQ_EP_INT, 0, hhid->Report_buf)) > 0) { USBD_HID_GetReportTrigger(0, 0, hhid->Report_buf, len); } }

注意:在DAP.h中,这里有个名为PIN_DELAY_SLOW的函数,它原本的实现是这样的:

__STATIC_FORCEINLINE void PIN_DELAY_SLOW (uint32_t delay) { uint32_t count; count = delay; while (--count); }

这里的空循环while (–count);会被GCC优化。我在StackOverflow中找到了一个好点子,它能正常工作但不是太合适,你有更好的方法吗?

__STATIC_FORCEINLINE void PIN_DELAY_SLOW (uint32_t delay) { uint32_t count; count = delay; while (--count) { /** * Empty loop will be totally omitted by GCC. * Search "How to prevent GCC from optimizing out a busy wait loop?" @ StackOverflow. * This solution isn't portable. Zach Lee */ __ASM(""); } }

至此,相关源码就介绍完毕,源码文件:

http://wiki.geniekits.com/downloads

参考来源:

http://wiki.geniekits.com/doku.php?id=usb_express:cmsis-dap

https://blog.csdn.net/qq_21506881/article/details/102633184

免责声明:本文部分素材来源网络,版权归原作者所有。如涉及作品版权问题,请与我联系删除

------------ END ------------

推荐阅读:

精选汇总 | 专栏 | 目录 | 搜索

C语言结构体描述BMP的文件格式

C语言printf()函数具体解释和安全隐患

关注微信公众号『嵌入式专栏』,底部菜单查看更多内容,回复“加群”按规则加入技术交流群。

点击“阅读原文”查看更多分享,欢迎点分享、收藏、点赞、在看。



【本文地址】


今日新闻


推荐新闻


CopyRight 2018-2019 办公设备维修网 版权所有 豫ICP备15022753号-3